package com.tlgen.zero.utils;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tlgen.orm.factory.Model;
import com.tlgen.orm.factory.QueryOperator;
import com.tlgen.zero.common.R;
import com.tlgen.zero.model.DataResult;
import com.tlgen.zero.model.FieldParam;
import com.tlgen.zero.model.RequestMethodAndUrl;
import org.apache.commons.collections4.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.tlgen.orm.utils.StringUtils.uncapUnderline;
import static com.tlgen.zero.utils.FileUtils.getAFile;
import static com.tlgen.zero.utils.FileUtils.getFileCompleteClassName;
import static com.tlgen.zero.utils.ReflectionUtils.*;
import static com.tlgen.zero.utils.ScanUtils.getExtendApiUrls;

public class ZeroUtils {

    /**
     * 零代码入口逻辑
     *
     * @param request
     * @return
     */
    public static DataResult startZero(HttpServletRequest request) {
        if (Objects.equals("GET", request.getMethod().toUpperCase())) {
            return doGet(request);
        } else if (Lists.newArrayList("POST", "PUT", "DELETE").contains(request.getMethod().toUpperCase())) {
            return doPostPutDelete(request);
        }
        return null;
    }

    /**
     * 处理 GET 请求
     *
     * @param request
     * @return
     */
    public static DataResult doGet(HttpServletRequest request) {
        DataResult dataResult = new DataResult();
        String url = request.getRequestURI();
        String path = url.substring(url.lastIndexOf("/"));
        // 判断是否是 selectById
        if (url.contains("/selectById/")) {
            path = "/selectById";
        }
        int selectFlag = 0;
        int selectIndex = -1;
        switch (path) {
            case "/selectOne":
                selectIndex = url.indexOf("/selectOne");
                break;
            case "/selectPage":
                selectIndex = url.indexOf("/selectPage");
                selectFlag = 1;
                break;
            case "/select":
                selectIndex = url.indexOf("/select");
                selectFlag = 2;
                break;
            case "/selectById":
                selectIndex = url.indexOf("/selectById");
                selectFlag = 3;
                break;
            default:  // 自定义扩展接口
                selectFlag = 4;
                break;
        }
        // selectOne || selectPage || select
        if (4 != selectFlag) {
            dataResult.setData(getBaseSelectOneSelectPageSelectData(request, dataResult, selectIndex, selectFlag));
            return dataResult;
        } else {
            dataResult.setData(getExtendApiUrlData(request, dataResult, url));
            return dataResult;
        }
    }

    /**
     * 处理 POST、PUT、DELETE 请求
     *
     * @param request
     * @return
     */
    public static DataResult doPostPutDelete(HttpServletRequest request) {
        DataResult dataResult = new DataResult();
        String url = request.getRequestURI();
        String path = url.substring(url.lastIndexOf("/"));
        // 判断是否是 deleteById
        if (url.contains("/deleteById/")) {
            path = "/deleteById";
        }
        // 判断是否是 deleteLogicalById
        if (url.contains("/deleteLogicalById/")) {
            path = "/deleteLogicalById";
        }
        int flag = 0;
        int postIndex = -1;
        switch (path) {
            case "/insert":
                postIndex = url.indexOf("/insert");
                break;
            case "/update":
                postIndex = url.indexOf("/update");
                flag = 1;
                break;
            case "/updateSelective":
                postIndex = url.indexOf("/updateSelective");
                flag = 2;
                break;
            case "/delete":
                postIndex = url.indexOf("/delete");
                flag = 3;
                break;
            case "/deleteById":
                postIndex = url.indexOf("/deleteById");
                flag = 4;
                break;
            case "/deleteLogicalById":
                postIndex = url.indexOf("/deleteLogicalById");
                flag = 5;
                break;
            default:
                flag = 6;
                break;
        }
        // insert || update || delete
        if (6 != flag) {
            dataResult.setData(getBaseInsertUpdateDeleteData(request, dataResult, postIndex, flag));
            return dataResult;
        } else {
            dataResult.setData(getExtendApiUrlData(request, dataResult, url));
            return dataResult;
        }
    }

    /**
     * 处理 GET 请求中查询单笔、分页查询列表、查询列表
     *
     * @param request
     * @param dataResult
     * @param selectIndex
     * @param selectFlag
     * @return
     */
    private static R getBaseSelectOneSelectPageSelectData(HttpServletRequest request, DataResult dataResult, int selectIndex, int selectFlag) {
        dataResult.setFlag(1); // 是否是可寻接口 0.否 1 .是
        if (selectIndex != -1) {
            String tmp = request.getRequestURI().substring(0, selectIndex);
            String className = tmp.substring(tmp.lastIndexOf("/") + 1);
            String completeClassName = getFileCompleteClassName(getAFile(className));
            // 如果找不到 model 类, 则走后续的 controller 逻辑
            if (StrUtil.isBlank(completeClassName)) {
                dataResult.setFlag(0); // 是否是可寻接口 0.否 1 .是
                return R.ok();
            }
            // 获取表单参数
            List<String> fields = getFields(completeClassName);
            List<FieldParam> fieldParams = getParameterFieldParams(request);
            // 获取 json 参数
            if (fieldParams.isEmpty()) {
                fieldParams = getRequestJsonFieldParams(request);
            }
            // 获取 @PathVariable 参数
            String uri = request.getRequestURI();
            String id = "";
            if (uri.contains("/selectById/")) {
                String temp = uri.substring(uri.indexOf("/selectById/") + "/selectById/".length());
                String[] split = temp.split("/");
                if (split.length == 0) {
                    dataResult.setFlag(0); // 是否是可寻接口 0.否 1 .是
                    return R.ok();
                }
                id = split[0];
            }
            // 调用数据库查询
            QueryOperator queryOperator = new QueryOperator<>();
            for (FieldParam fieldParam : fieldParams) {
                if (fields.contains(fieldParam.getName())) {
                    if (0 == selectFlag) { // 条件查询单笔, 查询条件使用等值查询
                        queryOperator.eq(uncapUnderline(fieldParam.getName()), fieldParam.getValue());
                    }
                    if (1 == selectFlag || 2 == selectFlag) { // 条件查询列表, 查询条件的字段使用模糊查询
                        queryOperator.like(uncapUnderline(fieldParam.getName()), fieldParam.getValue());
                    }
                }
            }
            switch (selectFlag) {
                case 0: // 条件查询单笔数据
                    return R.ok(Model.selectOne(queryOperator, getClassByCompleteClassName(completeClassName)), "查询成功!");
                case 1: // 条件分页查询数据列表
                    String pageNum = request.getParameter("pageNum");
                    String pageSize = request.getParameter("pageSize");
                    return R.ok(Model.selectPage(Integer.valueOf(pageNum), Integer.valueOf(pageSize),
                            queryOperator, getClassByCompleteClassName(completeClassName)), "查询成功!");
                case 2: // 条件查询数据列表
                    return R.ok(Model.select(queryOperator, getClassByCompleteClassName(completeClassName)), "查询成功!");
                case 3: // 根据 ID 查询单笔数据
                    return R.ok(Model.selectById(id, getClassByCompleteClassName(completeClassName)), "查询成功!");
            }
        }
        return R.ok(null, "查询成功!");
    }

    /**
     * 处理基本的新增、修改、删除逻辑
     *
     * @param request
     * @param dataResult
     * @param postIndex
     * @param flag
     * @return
     */
    private static R getBaseInsertUpdateDeleteData(HttpServletRequest request, DataResult dataResult, int postIndex, int flag) {
        dataResult.setFlag(1); // 是否是可寻接口 0.否 1 .是
        if (postIndex != -1) {
            String url = request.getRequestURI();
            String tmp = url.substring(0, postIndex);
            String className = tmp.substring(tmp.lastIndexOf("/") + 1);
            String completeClassName = getFileCompleteClassName(getAFile(className));
            // 如果找不到 model 类, 则走后续的 controller 逻辑
            if (StrUtil.isBlank(completeClassName)) {
                dataResult.setFlag(0); // 是否是可寻接口 0.否 1 .是
                return R.ok();
            }
            List<String> fields = getFields(completeClassName);
            List<FieldParam> fieldParams = Lists.newArrayList();
            Object object = getPostPutDeleteParams(request, fieldParams, completeClassName);
            if (Objects.nonNull(object) && CollectionUtils.isNotEmpty(fieldParams)) {
                switch (flag) {
                    case 0: // 新增
                        Model.save(object);
                        break;
                    case 1: // 修改
                        Model.update(object);
                        break;
                    case 2: // 选择更新
                        Model.updateSelective(object);
                        break;
                    case 3: // 根据字段条件进行物理删除
                        QueryOperator queryOperator = new QueryOperator<>();
                        // id 物理删除、条件物理删除均支持
                        for (FieldParam fieldParam : fieldParams) {
                            if (fields.contains(fieldParam.getName())) {
                                queryOperator.eq(fieldParam.getName(), fieldParam.getValue());
                            }
                        }
                        Model.delete(queryOperator, object.getClass());
                        break;
                    case 4: // 根据 id 进行物理删除
                        Model.deleteById(fieldParams.get(0).getValue(), object.getClass());
                        break;
                    case 5: // 根据 id 进行逻辑删除
                        Model.deleteLogicalById(fieldParams.get(0).getValue(), object.getClass());
                        break;
                }
            }
        }
        return R.ok(null, "操作成功!");
    }

    /**
     * 找不到增删改查的基本接口时, 查找自定义的扩展接口
     *
     * @param request
     * @param dataResult
     * @param url
     * @return
     */
    private static R getExtendApiUrlData(HttpServletRequest request, DataResult dataResult, String url) {
        // 自定义扩展接口
        List<RequestMethodAndUrl> extendApiUrls = getExtendApiUrls();
        RequestMethodAndUrl requestMethodAndUrl = extendApiUrls.stream()
                .filter(x -> url.contains(x.getUrl()))
                .findFirst()
                .orElse(null);
        if (Objects.nonNull(requestMethodAndUrl)) { // 非 404
            List<FieldParam> requestFieldParams = getRequestFieldParams(request);
            // 设置参数类型
            if (Objects.equals("application/json", request.getContentType())) {
                requestMethodAndUrl.setBodyType(0); // 请求体类型 0.application-json 1.form-data 2.@PathVariable 3.Object 0bj
                Object[] args = new Object[1];
                String requestJson = requestFieldParamsToJsonString(requestFieldParams);
                args[0] = requestJson;
                requestMethodAndUrl.setArgs(args);
            } else if (request.getContentType().contains("multipart/form-data")) {
                requestMethodAndUrl.setBodyType(1);
                // 设置参数
                Object[] args = new Object[requestFieldParams.size()];
                for (int i = 0; i < requestFieldParams.size(); i++) {
                    args[i] = requestFieldParams.get(i);
                }
                requestMethodAndUrl.setArgs(args);
            }
            // 调用方法
            dataResult.setData(invokeHttpRequest(requestMethodAndUrl));
            return R.ok(invokeHttpRequest(requestMethodAndUrl));
        } else {
            // 找不到零代码可寻的接口, 走 controller 标识
            dataResult.setFlag(0); // 是否是可寻接口 0.否 1 .是
            return R.ok();
        }
    }

    /**
     * 获取请求中表单中参数, 并组装成 List<FieldParam>
     *
     * @param request
     */
    private static List<FieldParam> getParameterFieldParams(HttpServletRequest request) {
        List<FieldParam> fieldParams = Lists.newArrayList();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = parameterNames.nextElement();
            String[] parameterValue = request.getParameterValues(parameterName);
            if (Objects.nonNull(parameterValue)) {
                FieldParam fieldParam = new FieldParam();
                fieldParam.setName(parameterName);
                fieldParam.setValue(parameterValue[0]);
                fieldParams.add(fieldParam);
            }
        }
        return fieldParams;
    }

    /**
     * 获取请求体中的参数, 并组装成 List<FieldParam>
     *
     * @param request
     * @return
     */
    private static List<FieldParam> getRequestJsonFieldParams(HttpServletRequest request) {
        List<FieldParam> fieldParams = Lists.newArrayList();
        String requestJson = getRequestJson(request);
        JSONObject jsonObject = JSONObject.parseObject(requestJson);
        if (Objects.nonNull(jsonObject)) {
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                if (Objects.nonNull(entry.getValue())) {
                    FieldParam fieldParam = new FieldParam();
                    fieldParam.setName(entry.getKey());
                    fieldParam.setValue(entry.getValue());
                    fieldParams.add(fieldParam);
                }
            }
        }
        return fieldParams;
    }

    /**
     * 获取接口请求参数
     *
     * @param request
     * @return
     */
    private static List<FieldParam> getRequestFieldParams(HttpServletRequest request) {
        // 获取表单参数
        List<FieldParam> fieldParams = getParameterFieldParams(request);
        // 获取 json 参数
        if (fieldParams.isEmpty()) {
            fieldParams = getRequestJsonFieldParams(request);
        }
        return fieldParams;
    }

    /**
     * 将接口请求参数转换成 JSON 字符串
     *
     * @param requestFieldParams
     * @return
     */
    private static String requestFieldParamsToJsonString(List<FieldParam> requestFieldParams) {
        JSONObject jsonObject = new JSONObject();
        for (FieldParam requestFieldParam : requestFieldParams) {
            jsonObject.put(requestFieldParam.getName(), requestFieldParam.getValue());
        }
        return jsonObject.toJSONString();
    }

    /**
     * 获取 POST、PUT、DELETE 接口的请求参数
     *
     * @param request
     * @param fieldParams
     * @param completeClassName
     * @return
     */
    private static Object getPostPutDeleteParams(HttpServletRequest request, List<FieldParam> fieldParams, String completeClassName) {
        // 获取表单参数
        fieldParams = getParameterFieldParams(request);
        // 获取 json 参数
        if (fieldParams.isEmpty()) {
            fieldParams = getRequestJsonFieldParams(request);
        }
        // 获取 @PathVariable 参数
        String uri = request.getRequestURI();
        if (uri.contains("/deleteById/")) {
            String tmp = uri.substring(uri.indexOf("/deleteById/") + "/deleteById/".length());
            String[] split = tmp.split("/");
            for (String value : split) {
                FieldParam fieldParam = new FieldParam();
                fieldParam.setName("id");
                fieldParam.setValue(value);
                fieldParams.add(fieldParam);
            }
        }
        if (uri.contains("/deleteLogicalById/")) {
            String tmp = uri.substring(uri.indexOf("/deleteLogicalById/") + "/deleteLogicalById/".length());
            String[] split = tmp.split("/");
            for (String value : split) {
                FieldParam fieldParam = new FieldParam();
                fieldParam.setName("id");
                fieldParam.setValue(value);
                fieldParams.add(fieldParam);
            }
        }
        // 获取有值的对象
        return ReflectionUtils.fieldParamsToObject(fieldParams, completeClassName);
    }

    /**
     * 获取请求体中的 JSON 数据
     *
     * @param request
     * @return
     */
    public static String getRequestJson(HttpServletRequest request) {
        StringBuilder buffer = new StringBuilder();
        try {
            BufferedReader reader = request.getReader();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return buffer.toString();
    }

}
